package brau.view.singleton;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.openddr.simpleapi.oddr.ODDRService;
import org.openddr.simpleapi.oddr.documenthandler.VocabularyHandler;
import org.openddr.simpleapi.oddr.model.ODDRHTTPEvidence;
import org.openddr.simpleapi.oddr.model.vocabulary.VocabularyProperty;
import org.w3c.ddr.simple.Evidence;
import org.w3c.ddr.simple.PropertyValue;
import org.w3c.ddr.simple.Service;
import org.w3c.ddr.simple.ServiceFactory;
import org.w3c.ddr.simple.exception.InitializationException;
import org.w3c.ddr.simple.exception.NameException;
import org.w3c.ddr.simple.exception.ValueException;
import org.xml.sax.SAXException;


public class DdrSingleton {

	private static DdrSingleton singletonObject;

    private Service identificationService = null;
    private VocabularyHandler vocabularyHandler = new VocabularyHandler();
	
	private DdrSingleton() {

		System.out.println("Initialize filter");
		Properties initializationProperties = new Properties();

		try {
			// Carrega as informações dos dispositivos (DDR) - Arquivos xml
		    carregarArquivosXml(initializationProperties, FacesContext.getCurrentInstance().getExternalContext());  
		    
		    // instancia o servico de identificação do dispositivo
		    identificationService = ServiceFactory.newService("org.openddr.simpleapi.oddr.ODDRService",initializationProperties.getProperty(ODDRService.ODDR_VOCABULARY_IRI), initializationProperties);
		} catch (Exception ex) {
		    throw new RuntimeException(ex);
		}
    	
    }  

    private void carregarArquivosXml(Properties initialProp, ExternalContext extContext) throws IOException {
		//      initializationProperties.put("oddr.ua.device.builder.stream", 								context.getResourceAsStream("/WEB-INF/classes/oddr/BuilderDataSource.xml"));
		//      initializationProperties.put("oddr.ua.device.datasource.stream", 								context.getResourceAsStream("/WEB-INF/classes/oddr/DeviceDataSource.xml"));
		//      initializationProperties.put("oddr.ua.browser.datasource.stream", 							context.getResourceAsStream("/WEB-INF/classes/oddr/BrowserDataSource.xml"));
		//      initializationProperties.put("oddr.ua.operatingSystem.datasource.stream", 					context.getResourceAsStream("/WEB-INF/classes/oddr/OperatingSystemDataSource.xml"));
		//      initializationProperties.put("ddr.vocabulary.core.stream", 									context.getResourceAsStream("/WEB-INF/classes/oddr/coreVocabulary.xml"));
		//      initializationProperties.put("oddr.limited.vocabulary.stream", 								context.getResourceAsStream("/WEB-INF/classes/ens99LimitedVocabulary.xml"));
		//      initializationProperties.put("oddr.vocabulary.stream", 					new InputStream[] {	context.getResourceAsStream("/WEB-INF/classes/oddr/oddrVocabulary.xml")});
		//      initializationProperties.put("oddr.ua.device.builder.patch.streams", 		new InputStream[] {	context.getResourceAsStream("/WEB-INF/classes/oddr/BuilderDataSourcePatch.xml") } );
		//      initializationProperties.put("oddr.ua.device.datasource.patch.streams", 	new InputStream[] {	context.getResourceAsStream("/WEB-INF/classes/oddr/DeviceDataSourcePatch.xml") } );
    	
    	initialProp.load(extContext.getResourceAsStream("/WEB-INF/classes/oddr.properties")); // carrega as informações de configuração da aplicação    	

    	Properties streamProperties = new Properties();
	    Set<Entry<Object, Object>> mapProp = initialProp.entrySet();
	    for (Entry<Object, Object> entry : mapProp) {
			String key = (String) entry.getKey();
			key = key.replace("path", "stream");
			String val = (String) entry.getValue();
		    if (val.toLowerCase().endsWith("xml")) {
		    	if (key.contains("patch") || key.contains("oddr.vocabulary.stream") ) {
		    		streamProperties.put(key, new InputStream[] { extContext.getResourceAsStream(val) } );
		    	} else {
		    		streamProperties.put(key, extContext.getResourceAsStream(val) );
		    		if (key.contains("limited.vocabulary")) {
	    	            try {
	    	            	// carrega em vocavularyHandler as propriedades definidas no limitedVocabulary
							parseVocabularyFromStream(vocabularyHandler, key, (InputStream) extContext.getResourceAsStream(val));
						} catch (InitializationException e) {
							e.printStackTrace();
						}
		    		}
		    	}
		    }
		}
	    if (streamProperties.size() > 0) initialProp.putAll(streamProperties);
    }
    

    private void parseVocabularyFromStream(VocabularyHandler vocabularyHandler, String prop, InputStream inputStream) throws InitializationException {
        InputStream stream = null;
        SAXParser parser = null;
        stream = inputStream;
        
        try {
            parser = SAXParserFactory.newInstance().newSAXParser();
        } catch (ParserConfigurationException ex) {
            throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalStateException("Can not instantiate SAXParserFactory.newInstance().newSAXParser()"));

        } catch (SAXException ex) {
            throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new IllegalStateException("Can not instantiate SAXParserFactory.newInstance().newSAXParser()"));
        }

        try {
            parser.parse(stream, vocabularyHandler);
        } catch (SAXException ex) {
            throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new RuntimeException("Can not parse document in property: " + prop));

        } catch (IOException ex) {
            throw new InitializationException(InitializationException.INITIALIZATION_ERROR, new RuntimeException("Can not open " + prop));
        }
        
        try {
            stream.close();
        } catch (IOException ex) {
            Logger.getLogger(ODDRService.class.getName()).log(Level.WARNING, null, ex);
        }

        parser = null;
    }
    
	
	public static synchronized DdrSingleton getInstance() {
		if (singletonObject == null) singletonObject = new DdrSingleton();
		return singletonObject;
	}
	
	
	public Object clone() throws CloneNotSupportedException {
		throw new CloneNotSupportedException();
	}

	
	public Service getIdentificationService() {
		return identificationService;
	}

	
	public void carregarListDdr(String userAgent, List<PropertyValue> listDdr) {
    	Evidence evidence = new ODDRHTTPEvidence();
    	evidence.put("User-Agent", userAgent);
    	PropertyValue propertyValue = null;
    	
//    	Para cada propriedade relacionada do Limited Vocabulary, buscamos a informação no serviço da DDR e adicionamos à lista de Propriedades
    	for (Entry<String, VocabularyProperty> entry : vocabularyHandler.getVocabulary().getProperties().entrySet()) {
	    	VocabularyProperty vProp = entry.getValue();
			try {
				propertyValue = DdrSingleton.getInstance().getIdentificationService().getPropertyValue(evidence, vProp.getName());
				if (propertyValue.exists()) listDdr.add(propertyValue);
			} catch (NameException e1) {
				e1.printStackTrace();
			}
	    }

    }

    
    public PropertyValue getPropertyByName(String name, List<PropertyValue> listDdr) {
		if (name == null || name.trim().equals("")) return null;
		PropertyValue prop = null;
		for (PropertyValue element : listDdr) {
			if (element.getPropertyRef().getLocalPropertyName().equals(name)) {
				prop = element;
				break;
			}
		}
		return prop;
	}

	public String getStringValueByName(String name, List<PropertyValue> listDdr) {
		if (name == null || name.trim().equals("")) return null;
		PropertyValue element = getPropertyByName(name, listDdr);
		String value = null;
		try {
			value = element.getString();
		} catch (ValueException e) {
			e.printStackTrace();
		}
//				if (element.equals("xs:string")) {
//					((HttpServletRequest)request).setAttribute(vProp.getName(), propertyValue.getString());
//				} else if (vProp.getType().equals("xs:boolean")) {
//					((HttpServletRequest)request).setAttribute(vProp.getName(), propertyValue.getBoolean());
//				} else if (vProp.getType().equals("xs:nonNegativeInteger")) {
//					((HttpServletRequest)request).setAttribute(vProp.getName(), propertyValue.getInteger());
//				} else if (vProp.getType().equals("xs:enumeration")) {
//			    	String[] arrayEnum = propertyValue.getEnumeration();
//			    	String strEnum = "";
//			    	for (int i = 0; i < arrayEnum.length; i++) strEnum+=", " + arrayEnum[i];
//			    	if (strEnum.length() > 0) strEnum = strEnum.substring(2);
//					((HttpServletRequest)request).setAttribute(vProp.getName(), strEnum);
//				} 
		return value;
	}
	
}
